package com.aloda.ojuser.service.impl;

import cn.hutool.core.util.RandomUtil;
import com.aloda.ojcommon.constant.ErrorCode;
import com.aloda.ojcommon.constant.ExceptionConstant;
import com.aloda.ojcommon.constant.RedisConstant;
import com.aloda.ojcommon.constant.UserConstant;
import com.aloda.ojcommon.exception.ParamsException;
import com.aloda.ojcommon.exception.VerifyException;
import com.aloda.ojcommon.utils.CheckUtils;
import com.aloda.ojcommon.utils.NetUtils;
import com.aloda.ojcommon.utils.VerifyUtil;
import com.aloda.ojuser.manager.MailManager;
import com.aloda.ojuser.pojo.dto.EmailCodeDTO;
import com.aloda.ojuser.pojo.entity.Verify;
import com.aloda.ojuser.pojo.vo.VerifyVO;
import com.aloda.ojuser.properties.VerifyProperties;
import com.aloda.ojuser.service.VerifyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import javax.annotation.Resource;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Base64;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @Author aloda
 * @Date 2025/4/20 14:16
 */
@Slf4j
@Service
public class VerifyServiceImpl implements VerifyService {

    private static final String PATTERN_VERITY = "^[A-Za-z0-9]{4}$";

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private VerifyProperties verifyProperties;

    @Resource
    private MailManager mailManager;

    /**
     * 获取图片验证码
     *
     * @param request
     * @return
     */
    @Override
    public VerifyVO getImgCode(HttpServletRequest request) throws IOException {
        // 1.是否可以进行验证码获取
        // 获取用户的 IP 地址
        String clientIp = NetUtils.getIpAddress(request);
        // 替换冒号
        String sanitizedIp = clientIp.replace(":", "_");
        // 存储到 Redis
        Long temp = stringRedisTemplate.opsForSet().add(RedisConstant.VERIFY_IP + sanitizedIp, sanitizedIp);
        if (temp == null || temp == 0) {
            throw new VerifyException(ExceptionConstant.OPERATE_FREQUENTLY);
        }

        // 2.获取一个验证码
        Verify verify = new Verify();
        Object[] obj = VerifyUtil.newBuilder().build().createImage();
        verify.setCode(obj[0].toString());
        // 将图片输出为 Base64 字符串
        BufferedImage image = (BufferedImage) obj[1];
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ImageIO.write(image, "png", baos);
        byte[] imageBytes = baos.toByteArray();
        String base64Image = Base64.getEncoder().encodeToString(imageBytes);
        verify.setImg(base64Image);

        // 3.生成验证码对应的唯一key
        String key = UUID.randomUUID().toString();
        verify.setKey(key);

        // 4.将验证码存入redis 设置过期时间
        stringRedisTemplate.opsForValue().set(RedisConstant.VERIFY_KEY + key, verify.getCode(), verifyProperties.getVerifyTimeout(), TimeUnit.SECONDS);

        // 5.将验证码获取者ip存入redis 设置过期时间 防止同一客户端频繁获取
        stringRedisTemplate.expire(RedisConstant.VERIFY_IP + sanitizedIp, verifyProperties.getVerifyColdTime(), TimeUnit.SECONDS);

        // 6.返回验证码
        VerifyVO verifyVO = new VerifyVO();
        verifyVO.setKey(verify.getKey());
        verifyVO.setImg(verify.getImg());
        return verifyVO;
    }

    /**
     * 获取邮箱验证码
     *
     * @param emailCodeDTO
     */
    @Override
    public boolean getEmailCode(EmailCodeDTO emailCodeDTO) {
        // 1.校验邮箱格式
        String email = emailCodeDTO.getEmail();
        if (!CheckUtils.checkEmail(email)) {
            throw new ParamsException(ErrorCode.PARAMS_ERROR.getCode(), ExceptionConstant.EMAIL_ERROR);
        }
        // 2.校验验证码
        checkVerify(emailCodeDTO.getCode(), emailCodeDTO.getKey(), stringRedisTemplate);
        // 3.校验操作是否频繁
        String key = RedisConstant.EMAIL_CODE_KEY + DigestUtils.md5DigestAsHex(email.getBytes());
        String result = stringRedisTemplate.opsForValue().get(key);
        if (result != null && !result.isEmpty()) {
            throw new VerifyException(ExceptionConstant.OPERATE_FREQUENTLY);
        }
        // 4.生成验证码
        String code = RandomUtil.randomNumbers(UserConstant.EMAIL_CODE_LENGTH);
//        boolean b = mailManager.sendVerificationCodeMail(email, code); todo: 开发环境先使用日志获取验证码
        // 模拟延迟
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        if (true) {
            // 6.发送成功将验证码保存到redis
            stringRedisTemplate.opsForValue().set(key, code, verifyProperties.getEmailVerifyTimeout(), TimeUnit.MINUTES);
            log.info("{}发送邮箱验证码：{}", email, code);
            return true;
        }
        return false;
    }

    /**
     * 校验验证码
     *
     * @param code                验证码
     * @param key                 验证码key
     * @param stringRedisTemplate stringRedisTemplate
     */
    public static void checkVerify(String code, String key, StringRedisTemplate stringRedisTemplate) {
        if (code == null || !code.matches(PATTERN_VERITY)) {
            throw new VerifyException(ExceptionConstant.VERIFY_ERROR);
        }
        String verify = stringRedisTemplate.opsForValue().get(RedisConstant.VERIFY_KEY + key);
        if (verify == null) {
            throw new VerifyException(ExceptionConstant.VERIFY_TIMEOUT);
        }
        if (!verify.equalsIgnoreCase(code)) {
            stringRedisTemplate.delete(RedisConstant.VERIFY_KEY + key);
            throw new VerifyException(ExceptionConstant.VERIFY_ERROR);
        }
        stringRedisTemplate.delete(RedisConstant.VERIFY_KEY + key);
    }
}
